<?php 
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
* @package DomAr
* @subpackage Plugin
*/
class DomArSimpleOrder extends DomArPlugin {
	
	protected $pos;
	
	public function __construct( $pos ) {
		$this->pos = (int)$pos;
	}
	/**
	 * Swaps current and next element
	 */
	public function increase() {
		$this->getProperty()->increase( $this->getParent() );
	}
	/**
	 * Swaps current and previous element
	 */
	public function decrease() {
		$this->getProperty()->decrease( $this->getParent() );
	}
	/**
	 * Moves current element after given position
	 * @TODO
	 * @param Int $id
	 */
	public function toAfter( $pos ) {
		throw new RuntimeException('Not implemented');
	}
	/**
	 * Moves current element before given position
	 * @TODO
	 * @param Int $id
	 */
	public function toBefore( $pos ) {
		throw new RuntimeException('Not implemented');
	}
	/**
	 * Returns next object according to order.
	 * 
	 * @return DomArObject
	 */
	public function getNext() {
		return $this->getProperty()->getNext( $this->getParent() );
	}
	/**
	 * Returns previous object according to order.
	 * 
	 * @return DomArObject
	 */
	public function getPrev() {
		return $this->getProperty()->getPrev( $this->getParent() );
	}
	/**
	 * Counts objects that order value is bigger than current item
	 * 
	 * @return Int
	 */
	public function countNext() {
		return $this->getProperty()->countNext( $this->getParent() );
	}
	/**
	 * Counts objects that order value is smaller than current item
	 * 
	 * @return Int
	 */
	public function countPrev() {
		return $this->getProperty()->countPrev( $this->getParent() );
	}

	/**
	 * Moves current element to first
	 */
	public function toMin() {
		$this->getProperty()->toMin( $this->getParent() );
	}
	/**
	 * Moves current element to last
	 */
	public function toMax() {
		$this->getProperty()->toMax( $this->getParent() );
	}
	/**
	 * Set current position value
	 * @param Int $pos
	 */
	public function setPos( $pos ) {
		$this->pos = (int)$pos;
		$this->changed();
	}
	/**
	 * @param String $var
	 * @return Mixed
	 */
	public function __get( $var ) {
		return $this->$var;
	}
	/**
	 * Resets current order value. Basicly moves element to last without affecting anyone else.
	 */
	public function reset() {
		$this->getProperty()->reset( $this->getParent() );
	}
	/**
	 * Resets all order values. Preserves current order but changes values to perfrect values.
	 * For example if order values are 1,5,8,10 then they will be changed to 1,2,3,4
	 */
	public function resetGroup() {
		$this->getProperty()->resetGroup( $this->getParent() );
	}
}


/**
* @link http://inditel.ee/
* @copyright 2007-2009 Inditel Meedia OÜ
* @author Oliver Leisalu <oliver@inditel.ee>
* @package DomAr
* @subpackage Plugin
*/
class DomArProperty_SimpleOrder extends DomArProperty_Plugin implements IDomArPropertyAlterTableListener,IDomArPropertyUpdateListener,IDomArPropertyInsertListener {
	
	protected $groupColumnNames = array();
	
	protected $step;
	
	public function __construct( $name, $groupColumnNames = NULL, $step = 10 ) {
		parent::__construct( $name );
		
		if( !is_array( $groupColumnNames ) && $groupColumnNames != NULL )
			$groupColumnNames = array( $groupColumnNames );
		
		if( is_array($groupColumnNames) ) {
			foreach( $groupColumnNames as $name ) {
				if( strtolower($name) != 'null' ) {
					$this->groupColumnNames[] = $name;
				}
			}
		}
		$this->step = (int)$step;
	}
	
	public function init( DomArObject $object, array &$values ) {
		return (int)$values[$this->name];
	}
	
	public function load( DomArObject $object, &$propertyValue ) {
		$r = new DomArSimpleOrder( $propertyValue );
		$r->changeParent( $object, $this );
		return $r;
	}
	
	public function isLoaded( DomArObject $object, &$propertyValue ) {
		return is_object($propertyValue);
	}
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		return ($propertyValue === 'max' or $propertyValue === 'min' or is_numeric($propertyValue) && $propertyValue >= 0 or is_object($propertyValue) && spl_object_hash($propertyValue) == spl_object_hash($object->{$this->name})) ? 0 : 1;
	}
	
	public function insert( DomArObject $object, &$propertyValue, DomArValueSet $set  ) {

		$this->reset( $object );
		$set->set( $this->name, (int)$object->{$this->name}->pos );
	}
	
	public function update( DomArObject $object, &$propertyValue, DomArValueSet $set  ) {
		$set->set( $this->name, (int)$object->{$this->name}->pos );
	}

	public function alterTable( DomArTable $table ) {
		$table->changeColumn( $this->name, 'int(11)', true );
	
		//$table->addIndex( $table->name . '_' . $this->relationProperty.'_'.$this->name, array($this->relationProperty, $this->name),'unique');
		$columns = $this->groupColumnNames;
		$columns[] = $this->name;

		$table->addIndex( $table->tableName . '_' .$this->name, $columns, 'index' );
	}
	
	/**
	 * @param DomArObject $object
	 */
	private function getGroupWhereCondition( DomArObject $object ) {
		
		$queryParts = array();
		foreach( $this->groupColumnNames as $columnName ) {
			
			$value = $object->$columnName;
			if( $value instanceof DomArObject )
				$value = $value->id;
			
			
			if( $value === NULL )
				$value = 'IS NULL';
			else if( is_numeric($value) )
				$value = '= '.(float)$value.'';
			else
				$value = '= \''.addslashes($value).'\'';
				
			$queryParts[] = '`'.$columnName.'` '.$value;
		}

		if( $queryParts )
			return implode(' AND ', $queryParts );
		else
			return '1=1';
		
	}

	/**
	 * @param DomArObject $object
	 */
	public function increase( DomArObject $object ) {
		
		$next = $this->getNext( $object );

		if( $next ) {
			
			$memory = $next->{$this->name}->pos;
			$next->{$this->name}->setPos( $object->{$this->name}->pos );
			$object->{$this->name}->setPos( $memory );
			
			DomSql::query('UPDATE '.$object->getDBTable().' SET `'.$this->name.'` = '.$object->{$this->name}->pos.' WHERE id = '.$object->id.' LIMIT 1'); 
			DomSql::query('UPDATE '.$object->getDBTable().' SET `'.$this->name.'` = '.$next->{$this->name}->pos.' WHERE id = '.$next->id.' LIMIT 1'); 		
		}

	}
	/**
	 * @param DomArObject $object
	 */
	public function decrease( DomArObject $object ) {
		
		$prev = $this->getPrev( $object );
			
		if( $prev ) {
			
			$memory = $prev->{$this->name}->pos;
			$prev->{$this->name}->setPos( $object->{$this->name}->pos );
			$object->{$this->name}->setPos( $memory );
			
			DomSql::query('UPDATE '.$object->getDBTable().' SET `'.$this->name.'` = '.$object->{$this->name}->pos.' WHERE id = '.$object->id.' LIMIT 1'); 
			DomSql::query('UPDATE '.$object->getDBTable().' SET `'.$this->name.'` = '.$prev->{$this->name}->pos.' WHERE id = '.$prev->id.' LIMIT 1'); 		
		}
	}
	/**
	 * @param DomArObject $object
	 */
	public function toMin( DomArObject $object ) {
		
		$id = $object->id;
		
		$e = DomSql::selectOneQuery('
			SELECT `'.$this->name.'` as e
			FROM '.$object->getDbTable().'
			WHERE id = '.$id.' AND '.$this->getGroupWhereCondition($object).'
			LIMIT 1','e');
		
		$s = DomSql::selectQuery('
			SELECT id, `'.$this->name.'` as o
			FROM '.$object->getDbTable().'
			WHERE `'.$this->name.'` < '.$e.' AND '.$this->getGroupWhereCondition($object).'
			');
		
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = `'.$this->name.'`+'.$this->step.' WHERE `'.$this->name.'` < '.$e.' AND '.$this->getGroupWhereCondition($object).'');
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.$this->step.' WHERE id = '.$id.'  AND '.$this->getGroupWhereCondition($object).' LIMIT 1'); 
		
		foreach( $s as $item ) {
			$ob = call_user_func(array(get_class($object),'load'), $item['id'] );
			$ob->{$this->name}->setPos( $item['o']+$this->step );
		}
		$object->{$this->name}->setPos($this->step);
		
	}
	/**
	 * @param DomArObject $object
	 */
	public function toMax( DomArObject $object ) {

		$id = $object->id;
		
		$e = DomSql::selectOneQuery('
			SELECT `'.$this->name.'` as e
			FROM '.$object->getDbTable().'
			WHERE id = '.$id.' AND '.$this->getGroupWhereCondition($object).'
			LIMIT 1','e');
					
		$s = DomSql::selectQuery('
			SELECT id, `'.$this->name.'` as o
			FROM '.$object->getDbTable().'
			WHERE `'.$this->name.'` > '.$e.' AND '.$this->getGroupWhereCondition($object).'
			');	
		
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = `'.$this->name.'`-'.$this->step.' WHERE `'.$this->name.'` > '.$e.' AND '.$this->getGroupWhereCondition($object).'');
		
		$m = DomSql::selectOneQuery('SELECT MAX(`'.$this->name.'`) as m FROM '.$object->getDbTable().' WHERE '.$this->getGroupWhereCondition($object).' LIMIT 1','m');
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.($m+$this->step).' WHERE id = '.$id.' LIMIT 1'); 
		
		
		foreach( $s as $item ) {
			$ob = call_user_func(array(get_class($object),'load'), $item['id'] );
			$ob->{$this->name}->setPos( $item['o']-$this->step );
		}
		$object->{$this->name}->setPos($m+$this->step);
	}
	/**
	 * @param DomArObject $object
	 */
	public function reset( DomArObject $object ) {
		$maxPos = DomSql::selectOneQuery('SELECT MAX(`'.$this->name.'`) as n FROM '.$object->getDbTable().' WHERE '.$this->getGroupWhereCondition($object).'','n')+$this->step;
		$object->{$this->name}->setPos( $maxPos );
		//if( $object->id > 0 ) {
		//	DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.$maxPos.' WHERE id = '.$object->id.' LIMIT 1');
		//}
	}
	/**
	 * @param DomArObject $object
	 */
	public function resetGroup( DomArObject $object ) {
		$elements = call_user_func( array(get_class($object), 'query') )
			->where($this->getGroupWhereCondition($object))
			->orderBy( '`'.$this->name.'`' )
			->commit();

		foreach( $elements as $i => $element ) {
			$element->{$this->name}->setPos( ($i+1)*$this->step );
			DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.($i+$this->step).' WHERE id = '.$element->id.' LIMIT 1');
		}		
			
	}
	/**
	 * @param DomArObject $object
	 * @return DomArObject
	 */
	public function getPrev( DomArObject $object ) {
		return call_user_func( array(get_class($object), 'query') )
			->where( $this->getGroupWhereCondition($object) )
			->where('`'.$this->name.'` <= '.(int)$object->{$this->name}->pos.' AND id != '.(int)$object->id.'')
			->orderBy( '`'.$this->name.'` DESC' )
			->limit(1)
			->commit();
	}
	/**
	 * @param DomArObject $object
	 * @return DomArObject
	 */
	public function getNext( DomArObject $object ) {
		return call_user_func( array(get_class($object), 'query') )
			->where( $this->getGroupWhereCondition($object) )
			->where('`'.$this->name.'` >= '.(int)$object->{$this->name}->pos.' AND id != '.(int)$object->id.'')
			->orderBy( '`'.$this->name.'`' )
			->limit(1)
			->commit();
	}
	/**
	 * @param DomArObject $object
	 * @return Int
	 */
	public function countPrev( DomArObject $object ) {
		return (int)DomSql::selectOneQuery('
			SELECT count(*) as n 
			FROM '.call_user_func(array(get_class($object),'getDbTable')).' 
			WHERE '.$this->getGroupWhereCondition($object).' AND `'.$this->name.'` <= '.(int)$object->{$this->name}->pos.' AND id != '.(int)$object->id.'
		', 'n');
	}
	/**
	 * @param DomArObject $object
	 * @return Int
	 */
	public function countNext( DomArObject $object ) {
		return (int)DomSql::selectOneQuery('
			SELECT count(*) as n 
			FROM '.call_user_func(array(get_class($object),'getDbTable')).' 
			WHERE '.$this->getGroupWhereCondition($object).' AND `'.$this->name.'` >= '.(int)$object->{$this->name}->pos.' AND id != '.(int)$object->id.'
		', 'n');
	}

}
?>